package com.android.videotest.music;

import android.app.Notification;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.media.MediaBrowserServiceCompat;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class MusicService extends MediaBrowserServiceCompat {
    private static final String ROOT_ID = "/";
    private static final String EMPTY_ROOT_ID = "empty";
    private static final String TAG = "MediaService-App";
    private MediaSessionCompat mSession;
    private PlayerAdapter mPlayback;
    private MediaNotificationManager mMediaNotificationManager;
    private MusicService mService;
    private boolean mServiceInStartedState;
    private MediaSessionCallback mSessionCallback;

    @Override
    public void onCreate() {
        super.onCreate();
        mService = this;
        // 创建一个MediaSessionCompat
        mSession = new MediaSessionCompat(this, TAG);
        // 使用ACTION_PLAY设置初始PlaybackState，以便媒体按钮可以启动播放器
//        PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder()
//                .setActions(
//                        PlaybackStateCompat.ACTION_PLAY |
//                                PlaybackStateCompat.ACTION_PLAY_PAUSE);
//        mSession.setPlaybackState(stateBuilder.build());
        mSessionCallback = new MediaSessionCallback();
        mSession.setCallback(mSessionCallback);
        mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS);
        // 设置会话的令牌，以便客户端活动可以与其进行通信。
        setSessionToken(mSession.getSessionToken());

        mPlayback = new MediaPlayerAdapter(this, new MediaPlayerListener());
        mMediaNotificationManager = new MediaNotificationManager(this);
    }

    @Nullable
    @Override
    public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
        //（可选）控制指定程序包名称的访问级别。
        // 您需要编写自己的逻辑来执行此操作。
        if (allowBrowsing(clientPackageName, clientUid)) {
            // 返回一个根ID，客户端可以将其与onLoadChildren（）一起使用以检索内容层次结构。
            return new BrowserRoot(ROOT_ID, null);
        } else {
            // 客户端可以连接，但是此BrowserRoot是一个空层次结构，因此onLoadChildren不返回任何内容。这将禁用浏览内容的功能。
            return new BrowserRoot(EMPTY_ROOT_ID, null);
        }
    }

    private boolean allowBrowsing(String clientPackageName, int clientUid) {
        return clientPackageName.equals(getPackageName());
    }

    @Override
    public void onLoadChildren(@NonNull String parentMediaId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {

        // 不允许浏览
        if (TextUtils.equals(EMPTY_ROOT_ID, parentMediaId)) {
            result.sendResult(null);
            return;
        }

        // 例如，假设音乐目录已被加载/缓存。
        List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();

        // 检查这是否是根菜单：
        if (ROOT_ID.equals(parentMediaId)) {
            mediaItems = MusicLibrary.getMediaItems();
            // 为顶层构建MediaItem对象，
            // 并将它们放在mediaItems列表中...
        } else {
            // 检查传递的parentMediaId以查看我们在哪个子菜单中，
            // 并将该菜单的子级放到mediaItems列表中...
        }
        if (mSessionCallback != null) {
            // 为这个简单的示例排队所有媒体项目。
            for (final MediaBrowserCompat.MediaItem mediaItem : mediaItems) {
                mSessionCallback.onAddQueueItem(mediaItem.getDescription());
            }
            // 立即调用进行准备，这样按一下播放就可以了。
//            mSessionCallback.onPrepare();
        }
        //发送结果到onLoadChildren回调，你可以将其添加到UI
        result.sendResult(mediaItems);
    }


    /**
     * 处理来自媒体控制器的回调
     */
    class MediaSessionCallback extends MediaSessionCompat.Callback {
        private final List<MediaSessionCompat.QueueItem> mPlaylist = new ArrayList<>();
        private int mQueueIndex = -1;
        private MediaMetadataCompat mPreparedMedia;

        public MediaSessionCallback() {
            super();
            mSession.setQueue(mPlaylist);
        }

        @Override
        public void onPrepare() {
            if (mQueueIndex < 0 || mPlaylist.isEmpty()) {
                // Nothing to play.
                return;
            }

            final String mediaId = mPlaylist.get(mQueueIndex).getDescription().getMediaId();
            mPreparedMedia = MusicLibrary.getMetadata(mService, mediaId);
            Log.d(this.getClass().getName(), "onPrepare: " + mPreparedMedia.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
            mSession.setMetadata(mPreparedMedia);

            if (!mSession.isActive()) {
                mSession.setActive(true);
            }
            Log.d(TAG, "onPrepare() called");
        }

        @Override
        public void onPlay() {
            if (!isReadyToPlay()) {
                // 什么也不播放。
                return;
            }

            if (mPreparedMedia == null) {
                onPrepare();
            }

            mPlayback.playFromMedia(mPreparedMedia);
            Log.d(TAG, "onPlay() called");
        }

        @Override
        public void onPlayFromMediaId(String mediaId, Bundle extras) {
            mQueueIndex = extras.getInt("position");
            mPreparedMedia = MusicLibrary.getMetadata(mService, mediaId);
            mSession.setMetadata(mPreparedMedia);
            Log.d(TAG, "onPlayFromMediaId() called with: mediaId = [" + mediaId + "], extras = [" + extras + "]");
            onPlay();
        }

        @Override
        public void onPause() {
            Log.d(TAG, "onPause() called");
            mPlayback.pause();
        }

        @Override
        public void onSkipToNext() {
            MediaControllerCompat controller = mSession.getController();
            int shuffleMode = controller.getShuffleMode();
            int repeatMode = controller.getRepeatMode();
            if (repeatMode == PlaybackStateCompat.REPEAT_MODE_ALL) {
                mQueueIndex = (++mQueueIndex % mPlaylist.size());
            } else if (shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL) {
                Random random = new Random(mPlaylist.size() - 1);
                mQueueIndex = random.nextInt();
            } else if (shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_NONE || repeatMode == PlaybackStateCompat.REPEAT_MODE_ONE) {
                mQueueIndex = (++mQueueIndex % mPlaylist.size());
            }

//            int repeatMode = controller.getRepeatMode();
//            switch (repeatMode) {
//                case PlaybackStateCompat.REPEAT_MODE_NONE:
//                case PlaybackStateCompat.REPEAT_MODE_ALL:
//                case PlaybackStateCompat.REPEAT_MODE_GROUP:
//                    mQueueIndex = (++mQueueIndex % mPlaylist.size());
//                    break;
//                case PlaybackStateCompat.REPEAT_MODE_ONE:
//                    onPlay();
//                    return;
//            }

            mPreparedMedia = null;
            onPlay();
            Log.d(TAG, "onSkipToNext() called");
        }

        @Override
        public void onSkipToPrevious() {
            MediaControllerCompat controller = mSession.getController();
            int shuffleMode = controller.getShuffleMode();
            int repeatMode = controller.getRepeatMode();
            if (repeatMode == PlaybackStateCompat.REPEAT_MODE_ALL) {
                mQueueIndex = mQueueIndex > 0 ? mQueueIndex - 1 : mPlaylist.size() - 1;
            } else if (shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL) {
                Random random = new Random(mPlaylist.size() - 1);
                mQueueIndex = random.nextInt();
            } else if (shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_NONE || repeatMode == PlaybackStateCompat.REPEAT_MODE_ONE) {
                mQueueIndex = mQueueIndex > 0 ? mQueueIndex - 1 : mPlaylist.size() - 1;
            }

            mPreparedMedia = null;
            onPlay();
            Log.d(TAG, "onSkipToPrevious() called");
        }

        @Override
        public void onStop() {
            mPlayback.stop();
            mSession.setActive(false);
            Log.d(TAG, "stop() called");
        }

        @Override
        public void onSeekTo(long pos) {
            mPlayback.seekTo(pos);
            Log.d(TAG, "onSeekTo() called with: pos = [" + pos + "]");
        }

        @Override
        public void onSetRepeatMode(int repeatMode) {
            mSession.setRepeatMode(repeatMode);
        }

        @Override
        public void onSetShuffleMode(int shuffleMode) {
            mSession.setShuffleMode(shuffleMode);
        }

        @Override
        public void onAddQueueItem(MediaDescriptionCompat description) {
            Log.d(this.getClass().getName(), "onAddQueueItem:  mPlaylist.size = " + mPlaylist.size());
            mPlaylist.add(new MediaSessionCompat.QueueItem(description, description.hashCode()));
            mQueueIndex = (mQueueIndex == -1) ? 0 : mQueueIndex;
        }

        private boolean isReadyToPlay() {
            return (!mPlaylist.isEmpty());
        }
    }

    // MediaPlayerAdapter Callback: MediaPlayerAdapter state -> MusicService.
    public class MediaPlayerListener extends PlaybackInfoListener {

        private final ServiceManager mServiceManager;

        MediaPlayerListener() {
            mServiceManager = new ServiceManager();
        }

        @Override
        public void onPlaybackStateChange(PlaybackStateCompat state) {
            // 向MediaSession报告状态。
            mSession.setPlaybackState(state);

            // 管理此服务的启动状态。
            switch (state.getState()) {
                case PlaybackStateCompat.STATE_PLAYING:
                    mServiceManager.moveServiceToStartedState(state);
                    break;
                case PlaybackStateCompat.STATE_PAUSED:
                    mServiceManager.updateNotificationForPause(state);
                    break;
                case PlaybackStateCompat.STATE_STOPPED:
                    mServiceManager.moveServiceOutOfStartedState(state);
                    break;
                case PlaybackStateCompat.STATE_BUFFERING:
                    break;
                case PlaybackStateCompat.STATE_CONNECTING:
                    break;
                case PlaybackStateCompat.STATE_ERROR:
                    break;
                case PlaybackStateCompat.STATE_FAST_FORWARDING:
                    break;
                case PlaybackStateCompat.STATE_NONE:
                    break;
                case PlaybackStateCompat.STATE_REWINDING:
                    break;
                case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT:
                    break;
                case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS:
                    break;
                case PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM:
                    break;
            }
        }

        @Override
        public void onPlaybackCompleted() {
            if (mSessionCallback != null) {
                mSessionCallback.onSkipToNext();
            }
        }

        class ServiceManager {

            private void moveServiceToStartedState(PlaybackStateCompat state) {
                Notification notification =
                        mMediaNotificationManager.getNotification(
                                mPlayback.getCurrentMedia(), state, getSessionToken());

                if (!mServiceInStartedState) {
                    ContextCompat.startForegroundService(
                            MusicService.this,
                            new Intent(MusicService.this, MusicService.class));
                    mServiceInStartedState = true;
                }

                startForeground(MediaNotificationManager.NOTIFICATION_ID, notification);
            }

            private void updateNotificationForPause(PlaybackStateCompat state) {
                stopForeground(false);
                Notification notification =
                        mMediaNotificationManager.getNotification(
                                mPlayback.getCurrentMedia(), state, getSessionToken());
                mMediaNotificationManager.getNotificationManager()
                        .notify(MediaNotificationManager.NOTIFICATION_ID, notification);
            }

            private void moveServiceOutOfStartedState(PlaybackStateCompat state) {
                stopForeground(true);
                stopSelf();
                mServiceInStartedState = false;
            }
        }

    }
}